﻿/*
Copyright (c) 2009 Vratislav Cermak (vratacermak@gmail.com)
 
This file is part of TeachMe Project. See http://code.google.com/p/teach-me for more information.
 
TeachMe is an open source project. Using of TeachMe is free of charge.
It is distributed under GNU General Public License version 3 (GPLv3).
GPLv3 can be found at http://www.gnu.org/licenses/gpl.html.
 
This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE.  See the license for more details.
*/

using System;
using System.Diagnostics;
using System.Runtime.InteropServices;

namespace TeachMe.Application.Win32
{
    internal delegate IntPtr LowLevelMouseProc(int nCode, IntPtr wParam, IntPtr lParam);

    public sealed class MouseHook : EasyDispose
    {
        private bool _enabled;

        public static MouseHook Instance
        {
            get
            {
                return Singleton<MouseHook>.Instance;
            }
        }

        public MouseHook()
        {
            Proc = new LowLevelMouseProc(this.HookCallback);
        }

        private DateTimeOffset LastEvent { get; set; }

        public bool Enabled
        {
            get
            {

                return _enabled;
            }
            set
            {
                Logging.Debug(typeof(MouseHook), "Hooks are {0}.", value ? "enabled" : "disabled");

                _enabled = value;
            }
        }

        public IntPtr ReceiverHandle { get; set; }
        private LowLevelMouseProc Proc { get; set; }
        private SafeMouseHookHandle Handle { get; set; }
        public bool RespawnHooks { get; set; }

        public void Start()
        {
            using (Process currentProcess = Process.GetCurrentProcess())
            {
                using (ProcessModule currentModule = currentProcess.MainModule)
                {
                    if (currentModule != null)
                    {
                        Handle = NativeMethods.SetWindowsHookEx(Constants.WH_MOUSE_LL, Proc, NativeMethods.GetModuleHandle(currentModule.ModuleName), 0);
                    }
                }
            }
        }

        private void Restart()
        {
            if (Handle != null && !Handle.IsClosed)
            {
                Handle.Close();
            }

            Handle = null;

            Start();
        }

        protected override void Disposing()
        {
            if (Handle != null)
            {
                Handle.Dispose();
            }
        }

        public void Touch()
        {
            if (RespawnHooks && LastEvent.AddSeconds(2) < DateTimeOffset.Now)
            {
                Logging.Warn(GetType(), "Restarting mouse hooks.");
                Restart();
            }
        }

        private IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam)
        {
            if (RespawnHooks)
            {
                LastEvent = DateTimeOffset.Now;
            }

            if (Enabled && nCode <= 0)
            {
                MSLLHOOKSTRUCT hookData = (MSLLHOOKSTRUCT)Marshal.PtrToStructure(lParam, typeof(MSLLHOOKSTRUCT));
                NativeMethods.PostMessage(ReceiverHandle, (uint)wParam, hookData.Point.X, hookData.Point.Y);
            }
            return NativeMethods.CallNextHookEx(Handle, nCode, wParam, lParam);
        }
    }
}